home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 007 / bintel10.arc / BINTEL.ASM next >
Assembly Source File  |  1987-11-01  |  53KB  |  1,400 lines

  1. name    BINTEL
  2. page    78,132   ;set printer to compressed, 8 lines/inch [^O Esc '0' on Epson]
  3. title   BINTEL.COM utility to interconvert binary & Intel hex files
  4. subttl  --  Introduction, principles of program  --
  5.  
  6. .radix 16        ;default number base for assembler = hexadecimal
  7.  
  8. Version equ '1.01'
  9. Revised equ '10/10/87 16:10 '
  10.  
  11. ; ============== REVISION HISTORY ==============
  12. ; 1.00   2/10/87  original version that works
  13. ; 1.01   3/16/87  fix bug in CommandArgs subroutine
  14.  
  15. ;uploaded to BIX 11/1/87 in BINTEL10.ARC
  16.  
  17. comment `
  18.     MASM 4.00/5.00   PC/MS DOS 2.x      Original:  2/10/87 by  R. M. Baldwin.
  19.     COM format.  Compiled on IBM PC-clone w/ 448K, V20 CPU, 4.8 MHz.
  20.  
  21. To assemble with Microsoft Macro Assembler:
  22.     masm bintel;
  23.     link bintel;
  24.     exe2bin bintel bintel.com
  25.  
  26. USE:
  27.  1. To convert a binary file BINARY.COM:
  28.        bintel binary      ;creates BINARY.HEX in Intel format
  29.  2. To convert a hex file BINARY.HEX back to binary:
  30.        bintel binary /h   ;creates BINARY.BIN
  31.  3. Entering bintel with nothing or bintel ? gives a brief explanation.
  32.  
  33. PURPOSE:
  34.  
  35.   Converts a binary file to Intel hex format for transmission as Ascii file.  
  36. Main purpose is to download to Steve Ciarcia's Circuit Cellar Serial EPROM 
  37. Programmer (CCSP), which uses Intel Hex format.  Applications are programming 
  38. EPROMS for a Z-80 based process controller.  
  39.  
  40. BACKGROUND:
  41.   Intel format consists of a series of records called 'paragraphs', begun with 
  42. a colon (':') and ended with a CR.  For example: 
  43.  
  44. :100000003180083E89D303AFD300D301060711FF27    (1st paragraph -- 16 data bytes)
  45. :0D001000FF21FFFF19DA140010F73E10D396          (2nd paragraph -- 13 data bytes)
  46. :00000001FF                                    (last paragraph; no data bytes)
  47.  
  48. The meaning of the fields in each paragraph:
  49.  
  50. Colon  Data   Address of  Paragr  Data (pairs of nibbles)    CRC
  51. Delim  Count  1st data    Type     in ASCII form          (CheckSum)
  52. -iter           byte    (Attribute)
  53.  
  54.   :     10      0000        00      31 80 08 3E ...    FF     27
  55.   :     0D      0010        00      FF 21 FF FF ... D3        96
  56.   :     00      0000        01      {none}                    FF
  57.  
  58. All numbers are in upper-case Ascii hexadecimal representation. 
  59. Data count: # data bytes in the paragraph.
  60. Address:    address (offset) where 1st data byte goes.
  61. Para type:  00 = not the end yet, 01 = last paragraph.
  62. CRC:        CheckSum = 2's complement of the 8-bit addition of the binary 
  63.             values of all the other bytes. (ie the sum of the CRC plus all the 
  64.             other bytes = 0.)
  65.  
  66. [Reference: Bill Curlew, Circuit Cellar BBS msg #4221 (10/27/86)]
  67.  
  68. OUTLINE OF PROGRAM:
  69.   1. Get input file and command switch(es) from command line.
  70.   2. Decide whether Binary --> Intel  or  vice versa.
  71.   3. Open input file. 
  72.      Default file extension conventions:
  73.         binary file: BIN or COM  (priority: BIN > COM)
  74.         hex file:    HEX
  75.   4. Use buffers for input & output; allocate 3 X the space for the hex data.
  76.   5. Process binary file
  77.      a. Read a block from file into input buffer.
  78.      b. Read up to 16 bytes from buffer; if buffer exhausted, read block from 
  79.         disk. 
  80.      c. Store ':',then # bytes, then address, then '00', then the data bytes, 
  81.         then the checksum, all in ASCII representation, then CR in output 
  82.         buffer (+ LF for clarity).  
  83.      d. If buffer fills up before end, append buffer contents to output file. 
  84.      e. When end of file reached, append a null paragraph with  '01' as the 
  85.         paragraph attribute, ie ':00000001FF'.  
  86.  
  87.  5'. Process IntelHex file:
  88.      a. Read a block from file into input buffer.
  89.      b. Read bytes from buffer; if buffer empty, read block from disk. Skip 
  90.         over chars until paragraph start (':') found. Then read next 2 bytes = 
  91.         # data bytes.  
  92.      c. Read in address offset, compare with calculated value, error if no 
  93.         agreement.  Ignore the file address anyway.  Read paragraph attribute, 
  94.         store. Get all data bytes, store in output buffer.  
  95.      d. If buffer fills up before end, append buffer contents to output file. 
  96.      e. Check CRC with that calculated. Error if they don't agree.
  97.   6. Write buffer to output file and close all files.  
  98.  
  99. FUTURE ADDITION IDEAS:
  100.  1. Allow specifying start address.
  101.  2. Allow specifying groups of files with DOS wild cards in file spec; 
  102.       eg,  bintel proc*
  103.  3. Allow specifying a different drive\path and/or filename for output file.
  104.  4. Check for existing output files & confirm before overwriting.
  105.  
  106. EndComment `
  107.  
  108. subttl  --  Declarations, equates, macros  --
  109. page                 ;start a new page
  110. ;---  Equates  ---
  111. ;Ascii characters
  112. Bel     equ  7   ;^G console bell
  113. Tab     equ  9   ;^I
  114. LF      equ 0Ah  ;line feed ^J
  115. CR      equ 0Dh  ;carriage return ^M
  116. Eof     equ 1Ah  ;end of file ^Z
  117. Blank   equ 20h  ;space char
  118. Space   equ 20h  ;space char
  119.  
  120. ;DOS function calls
  121.  
  122. DOSver    equ 30h   ;get DOS version
  123. Terminate equ 4Ch   ;terminate with return code
  124. TTYout  equ  2      ;single char output to console (std out)
  125. Pstring equ  9      ;print string ending with '$' to console (for DOS 1.x)
  126.  
  127. ;File-related DOS calls
  128.  
  129. FileCreate  equ 3Ch   ;create or truncate file with AsciiZ
  130. FileOpen    equ 3Dh   ;open file with AsciiZ
  131. FileClose   equ 3Eh
  132. ReadFile    equ 3Fh   ;R/W file or device with AsciiZ spec
  133. WriteFile   equ 40h
  134.  
  135. ;Device handles
  136.  
  137. StdOut  equ 1       ;standard output (CON) redirectable
  138. StdErr  equ 2       ;standard error device (CON) (not redirected)
  139. ConOut  equ 2       ;non-redirectible output (CON)
  140.  
  141. ;---  program-specific equates ---
  142.  
  143. CommandTail equ 80h  ;address of command tail in PSP (Program Segment Prefix)
  144. SwitchChar  equ '/'  ;for adding commands from DOS
  145. HexCode     equ 'H'  ;command for processing hex file to binary
  146. Dot         equ '.'  ;for finding file extension
  147. ParStart    equ ':'  ;delimiter for beginning of IntelHex 'paragraph'
  148. ParEnd      equ CR   ;   "       "     end     "     "        "
  149.  
  150. DOSpath     equ 40h        ;max # chars in DOS path/file spec
  151.  
  152. RecLen      equ 10h        ;size of 'paragraph': default 16 bytes
  153. HexOverHead equ 13d        ;# extra bytes overhead for a hex paragraph; ie:
  154.                            ; ':nnaaaatt...cc'<CR><LF>
  155.                            ; (# Hex bytes) = (# binary bytes) * 2 + HexOverHead
  156. BinBufSize  equ 10h*RecLen       ;binary buffer size in 'paragraphs'
  157. HexBufSize  equ 3*BinBufSize     ;3* for hex (16 bytes binary -->45 in hex format)
  158.  
  159. ;--- Macros ---
  160.  
  161. DOScall Macro Function     ;; invoke int 21h with AH=function
  162.      mov AH, &Function
  163.      int 21h
  164. endM
  165.  
  166. Write   Macro Dev, Msg, Len ;;write Msg (Len bytes long) to Dev
  167.      mov DX, offset &Msg    ;;DS:DX points to address of buffer
  168.      mov CX, &Len           ;;CX = # bytes to write
  169.      mov BX, &Dev           ;;BX = device or file handle
  170.      DOScall writeFile      ;;write to file or device
  171. endM
  172.  
  173. Read    Macro Dev, Buf, Len ;;read Len bytes from Dev into Buf buffer
  174.      mov DX, offset &Buf
  175.      mov CX, &Len
  176.      mov BX, &Dev
  177.      DOScall readFile
  178. endM
  179.     
  180. EchoZ   macro  Str         ;;echo string Str terminated with binary 0
  181.      push SI               ;;save SI
  182.      mov SI, offset &Str   ;;subroutine will use string at DS:SI
  183.      call EchoString
  184.      pop SI
  185. endM
  186.  
  187. subttl  --  Main program -- Variable data area -- 
  188. page
  189. ;================
  190. Code    segment  para public 'CODE'
  191.  
  192.     assume CS:Code,DS:Code,ES:Code,SS:Code
  193.     org 100h         ;start at offset 100h for COM file
  194.  
  195. Main    proc far     ;main procedure
  196.  
  197.   jmp Main0          ;skip variable area
  198.  
  199. ;---  strings  ---
  200.  
  201. Header  db CR
  202. db 'BINTEL.COM Binary-Intel Hex Converter,  Version ', Version,' (1987) R. M. Baldwin'
  203. db CR, LF
  204. HeaderLen equ $-Header
  205.  
  206. db 'Last revision: ',Revised      ;makes it easy to keep track of .COM file.
  207.  
  208. ;---  variables  ---  (buffers and messages stored at end)
  209.  
  210. BinExt1     db '.BIN'      ;Default extension for binary file
  211. BinExt2     db '.COM'      ;alternate  "       "    "     "
  212. HexExt1     db '.HEX'      ;default extension for Ascii file
  213. HexExt2     db '.HEX'      ;alternate  "       "    "    "
  214.  
  215. ProcCode    db 0    ;0 means process binary file, non-zero means hex
  216. ErrorCode   db 0    ;Return code with DOS function 4Ch.
  217.                     ; Can check in DOS batch files:
  218.                     ; eg, 'IF NOT ERRORLEVEL 1 GOTO NOERROR'
  219.                 ;0 = no error
  220.                 ;1 = serious error,  2=warning, eg overflow in address counter.
  221.  
  222. InBufTail   dw 0    ;pointer to end of current data in input buffer
  223.  
  224. LastPar     db ParStart,'00000001FF', ParEnd   ;Last paragraph, always
  225.             db LF, Eof                         ; appended as last record.
  226. LastParLen  equ  $-LastPar                     ; Add Eof mark (^Z) for luck.
  227.  
  228. InFile      dw 0                 ;handle returned by DOS -- initialize to 0
  229. InFileName  db DOSpath dup(0)    ;Asciiz string for input file
  230.  
  231. OutFile     dw 0                 ;handle returned by DOS; pre-set to 0 as
  232.                                  ;  signal that file not opened yet 
  233. OutFileName db DOSpath dup(0)    ;AsciiZ for output file
  234.  
  235. LineCount   dw 1  ;Line # in hex file (start with #1; will always have atleast 1)
  236. ParaCount   dw 1  ;paragraph #
  237. Address     dw 0  ;address offset for first byte in paragraph ;NOTE: offset is
  238.                   ; from start of file; ie, COM files lose 100h offset.
  239. NumBytes    dw 0  ;# bytes in current paragraph. Use word to facilitate loading CX
  240. LastParagr  db 0  ;code to indicate last paragraph (0=no, 1=yes)
  241.  
  242. subttl  --  Main program --
  243. page
  244. ;-----------
  245. Main0:               ;start of main program
  246.     DOScall DOSver   ;check DOS version
  247.     cmp AL,2         ;DOS 2 or greater?
  248.     jae Main1        ;OK
  249. ;DOS<2.0:
  250.     mov DX,offset DOSerr   ;use DOS 1.x print string function
  251.     DOScall Pstring        ; to display error message
  252.     int 20h                ; & exit to operating system
  253.  
  254. Main1:
  255.     write StdOut,Header, HeaderLen     ;display header
  256.     call CommandArgs                   ;get command from PSP
  257.         ;this routine changes SI, DI, AL.
  258.         ;it sets carry flag to signify that either no file spec was entered on 
  259.         ; command line, or a '?' was entered, thus triggering explanation.
  260.                      ;IF SUCCESSFUL (Cy = 0),
  261.                      ;  InFileName = ParamStr(1),  
  262.                      ;  ProcCode   = 1 if hex switch entered, 0 otherwise.
  263.     jnc Main2                          ;Cy set triggers explanatory message.
  264.     write StdOut, HelpMsg, HelpLen     ;display explanatory message
  265.     jmp ErrExit                        ; & exit with 'error' code
  266.  
  267. Main2:               ;InFile now contains input file spec
  268.     mov AL,ProcCode  ;convert binary-->Intel or vice versa?
  269.     or  AL,AL        ;zero is default
  270.     jz  ProcBin      ;process binary file if 0 (default)
  271.     jmp ProcHex      ;if not zero, process hex-->binary (note jmp > 127 bytes)
  272.  
  273. subttl  ---  Process Binary File  ---
  274. ;++++++++++++++++
  275. ProcBin proc near    ;process a binary file
  276.  
  277.     mov BP, offset BinExt1 ;point to address of 1st def ext for Open subroutine
  278.     call OpenInput         ;try to open input binary file
  279.  ;on entry, BP = address of 1st default ext.; the 2nd is stored right after
  280.  ;on exit,  Cy flag will be set if error, clear if Ok
  281.  ;AX, CX, & SI are altered
  282.     jnc ProcBin1           ;Cy indicates file open error
  283.     jmp ErrExit            ;this is a long jump (> 127 bytes)
  284.  
  285. ProcBin1:
  286. ;FUTURE: add check for file already exists, overwrite (Y/N)?
  287.     mov BP, offset HexExt1 ;point to def ext for output file
  288.     call OpenOutFile       ;open output file & truncate
  289.     jnc ProcBin1a          ;Cy set on error
  290.     jmp ErrExit
  291.  
  292. ProcBin1a:
  293.     lea DI, HexBuffer      ;initialize pointer to output buffer
  294.     write StdOut, ProcBinMsg, ProcBinLen     ;report progress to console
  295.     echoZ InFileName
  296.     write StdOut, BinHead, BinHeadLen        ;header for paragraph address
  297.  
  298. ;at this point, both InFile & OutFile are open & ready to go
  299. ;DS:SI = input buffer pointer;   ES:DI = output buffer pointer
  300. ;InBufTail points to end of data in input buffer
  301. ;HexBuffer + HexBufSize is the end of the output buffer
  302.  
  303. ReadBlock:       ;read a block of data from input [binary] file
  304.                  ;BX= file handle (InFile)
  305.                  ;CX= # bytes requested = input buffer size (BinBufSize)
  306.                  ;DX= input buffer area (BinBuffer)
  307.                  ;IF SUCCESSFUL,
  308.                  ; AX=# bytes actually read, CX=# bytes requested (BinBufSize)
  309.                  ; if AX=0 then end of file reached.  
  310.                  ; Data stored starting at BinBuffer.
  311.                  ;IF UNSUCCESSFUL,  AX= error code,  Carry flag set.
  312.  
  313.     mov BX, InFile               ;input file
  314.     mov CX, BinBufSize           ;# bytes to [try to] read from file
  315.     mov DX, offset BinBuffer     ;point to input buffer
  316.     DOScall readFile             ;function 3Fh
  317.  
  318.     jnc ProcBin2     ;NOTE:  Carry flag set if error occured
  319.     call ReadError   ;error, display msg
  320.     jmp FinishOff    ; & exit
  321.  
  322. ProcBin2:            ;read Ok, no error
  323.     cmp AX,0         ;end of file?
  324.     jnz ProcBin3     ;no, continue; [short jump]
  325.     jmp LastParag    ;EOF, write last paragraph & finish [long jump > 127 bytes]
  326.  
  327. ProcBin3:                  ;continue processing file
  328.     lea SI, BinBuffer      ;start at beginning of input buffer
  329.  
  330. ;address of end of data area = InBuffer + # bytes read in - 1
  331. ;AX contains # bytes read in; SI points to next data byte to be read.
  332. ;InBufTail = AX + SI - 1
  333.  
  334.     add AX, SI       ;AX = # bytes + data pointer; points to address after last byte
  335.     dec AX           ;AX = address of last data byte
  336.     mov word ptr InBufTail, AX   ;store it
  337.  
  338. ;----------------  main processing routine   -------------------
  339.  
  340. ProcParag:           ;process next paragraph
  341. ;1st check to see if input buffer empty yet
  342.     mov CX, InBufTail   ;Address of last byte of data.
  343.     cmp SI,CX           ;Are we beyond the end?
  344.     ja  ReadBlock       ;Yes, input buffer exhausted, read another block from disk
  345.  
  346. ;calculate # bytes left in buffer = CX - (SI-1) = (CX-SI) + 1
  347.     sub CX, SI       ;CX= (CX-SI)
  348.     inc CX           ;CX= (CX-SI) + 1
  349.  
  350. ;determine # data bytes in this paragraph (CX contains # bytes)
  351.     mov AX,RecLen    ;# bytes in a paragraph
  352.     cmp CX, AX       ;(# bytes Left) <= (1 paragraph) ?
  353.     jbe ProcPar1     ;if less or equal, use value in CX for # data bytes;
  354.     mov CX, AX       ; else, use # bytes in a paragraph
  355.  
  356. ;CX contains # data bytes in this paragraph
  357.  
  358. ProcPar1:        ;see if there's room in output buffer for this paragraph
  359.       ;NOTE: DI points to next open position in output buffer
  360.       ;NOTE: # bytes required in output buffer = HexOverHead + 2*(# data bytes)
  361.     mov BX,CX    ;BX= (# data bytes)
  362.     shl BX,1     ;BX= 2* (# data bytes)
  363.     add BX,HexOverHead   ;BX= HexOverHead + 2 * (# data bytes) = # bytes req'd in output buffer
  364. ;NOTE:
  365. ;  End of output buffer = HexBuffer + HexBufSize
  366. ;  Last byte address +1 = DI + BX
  367.     mov AX, offset HexBuffer
  368.     add AX, HexBufSize           ;AX = address of end of output buffer
  369.     add BX, DI                   ;BX = address of last byte + 1
  370.     cmp BX, AX                   ;is there room?
  371.     jbe ProcPar2           ;yes, there's room, continue
  372.  
  373. ;not enough room, write output buffer to disk.
  374.     mov BP, offset HexBuffer     ;point to output buffer
  375.     call WriteOutput       ;NOTE: WriteOut proc preserves registers AX-DX, 
  376.                            ; resets DI to point to start of OutBuffer
  377.                            ; sets carry flag to indicate write error
  378.     jnc ProcPar2     ; no error, continue
  379.     jmp FinishOff    ;write error occured: -- abort.
  380.  
  381. ProcPar2:
  382.     xor BX, BX       ;BL=0; initialize check sum (CRC); will be kept in BL
  383.  
  384. ;NOTE: DI points to next position to store data in output buffer;
  385. ;      STOSx instructions automatically increment DI according to byte or word.
  386. ;   ConvHex subroutine converts byte in AL into the Ascii representation of the
  387. ;   byte in AX;  AH= char of most significant nibble, AL= least significant.
  388.  
  389. ;store paragraph beginning delimiter
  390.     mov AL, ParStart
  391.     stosb
  392. ;store # data bytes in this paragraph
  393.     mov AX, CX       ;AL now contains # bytes (since paragraphs always < 255)
  394.     add BL, AL       ;add to checksum
  395.     call ConvHex     ;convert byte in AL into Ascii representation in AX
  396.     stosw            ; & store it in output buffer
  397. ;store file address offset of this paragraph
  398.     mov AX, Address  ;get address value (16 bits)
  399. ;MSB
  400.     mov AL, AH       ;store high byte first
  401.     add BL, AL       ;add to checksum
  402.     call ConvHex     ;convert byte in AL into Ascii representation in AX
  403.     stosw            ; & store it
  404.     mov AX, Address  ;get address value again
  405. ;LSB
  406.     add BL, AL       ;AL= low byte; add to checksum
  407.     call ConvHex     ;convert
  408.     stosw            ; & store it
  409.  
  410.     call EchoAddr    ;echo paragraph address at (DI-4)
  411.                      ;routine preserves all regs
  412.  
  413. ;update address value -- 
  414.     mov AX, Address
  415.     add AX, CX                   ; add # bytes in current paragraph
  416.     mov word ptr Address, AX     ; store updated value of address
  417.     jnc ProcPar3                 ;check for overflow on large file from ADD instr.
  418.  
  419. ;if input binary file has more than 64K bytes, address field will overflow
  420. ;display error message as warning, but proceed with process
  421.     push AX      ;save registers used in writing msg
  422.     push BX
  423.     push CX
  424.     push DX
  425.  
  426.     write StdOut, OverflowMsg, OverflowLen
  427.     mov ErrorCode, 2       ;store code for 'non-fatal' error, address overflow
  428.  
  429.     pop DX       ;restore registers
  430.     pop CX
  431.     pop BX
  432.     pop AX
  433.  
  434. ProcPar3:            ;continue with processing:
  435. ;store paragraph attribute; will always be '00' because last paragraph done elsewhere
  436.     mov AX, 3030h    ;'00' in ASCII
  437.     stosw            ;NOTE: doesn't contribute to check sum
  438.  
  439. ;store data bytes -- NOTE: SI points to data byte in input buffer
  440. NextByte:            ;     CX = # data bytes to process
  441.     lodsb            ;load data byte into AL
  442.     add BL, AL       ;update check sum
  443.     call ConvHex     ;convert byte to Ascii representation in AX
  444.     stosw            ; & store it in output buffer
  445.     loop NextByte    ;get next byte
  446.  
  447. ;now take 2's complement of check sum
  448.     neg BL           ;CRC = - (check sum)
  449.     mov AL, BL       ;convert to ascii representation
  450.     call ConvHex
  451.     stosw            ; & store it
  452.  
  453.     mov AL, ParEnd   ;store the paragraph ending delimiter (carriage return)
  454.     stosb            ; in output buffer
  455.  
  456. ;could make this optional via switch /L eg.
  457.     mov AL,LF        ;Add a line feed for clarity in displaying file
  458.     stosb
  459.  
  460.     jmp ProcParag    ;go back & process next paragraph
  461. ;----------------
  462.  
  463. LastParag:       ;no more data, write last paragraph
  464.                  ;NOTE: last paragraph always same format, a null paragraph
  465.                  ;   with format LastPar = ':00000001FF {CR}{LF}'
  466.                  ;            LastParLen = HexOverHead bytes
  467. ;check if room for last paragraph
  468. ;NOTE: DI points to next open position in output buffer
  469.     mov BX, LastParLen     ; # bytes to write
  470.  
  471. ;NOTE:  End of output buffer = OutBuffer + HexBufSize
  472. ;       Last byte address +1 = DI + BX
  473.  
  474.     mov AX, offset HexBuffer
  475.     add AX, HexBufSize           ;AX = address of end of output buffer
  476.     add BX, DI                   ;BX = address of last byte + 1
  477.     cmp BX, AX                   ;is there room?
  478.     jbe LastPar1           ;yes, there's room, continue
  479. ;not enough room, write output buffer to disk.
  480.     mov BP, offset HexBuffer    ;WriteOut expects address of buffer in BP
  481.     call WriteOutput ;NOTE: WriteOut proc preserves registers AX-DX, 
  482.                      ; resets DI to point to start of OutBuffer, sets Cy if error.
  483.     jnc LastPar1     ;Cy=0 no error
  484.     jmp FinishOff    ;carry set: write error occured: -- abort.
  485.  
  486. LastPar1:            ;copy last paragraph string into output buffer
  487.     mov CX, LastParLen     ;# bytes to copy
  488.     mov SI, offset LastPar ;point to string to copy
  489.                            ;NOTE: DI still points to destination in OutBuffer
  490.     rep movsb              ;copy the string
  491. ;& write the output buffer to disk
  492.     mov BP, offset HexBuffer     ;output buffer address
  493.     call WriteOutput             ;write to disk  (registers are preserved)
  494.                                  ;if write error occured, ErrorCode=1
  495.     jmp FinishOff
  496.  
  497. ProcBin endP     ;end of process binary procedure
  498. subttl  ---  Process Intel Hex File  ---
  499. ;++++++++++++++++
  500.  
  501. ProcHex proc near    ;process an input file in Intel Hex format
  502.  
  503.     mov BP, offset HexExt1 ;point to address of 1st def ext for Open subroutine
  504.     call OpenInput         ;open input file -- (AX, CX, & SI are altered)
  505.     jnc ProcHex1           ;Cy indicates file open error
  506.     jmp ErrExit            ;this is a long jump (> 127 bytes)
  507.  
  508. ProcHex1:
  509. ; FUTURE: add check for file already exists, overwrite (Y/N)?
  510.     mov BP, offset BinExt1 ;default extension for output file passed in BP
  511.     call OpenOutFile       ;open output file & truncate (AX-DX, SI, DI are altered).
  512.     jnc ProcHex2           ;Cy set on error
  513.     jmp ErrExit            ;long jump
  514.  
  515. ProcHex2:
  516.     write StdOut, ProcHexMsg,ProcHexLen        ;report on progress
  517.     echoZ InFileName
  518.     write StdOut, HexHead, HexHeadLen          ;header for line # & paragraph #
  519.  
  520. ;initialize variables
  521.     lea DI, BinBuffer      ;initialize pointer to output buffer
  522.  
  523. ;at this point, input file = hexfile; output file = binary file, both open.
  524. ;DS:SI --> input buffer (HexBuffer), ES:DI --> output buffer (BinBuffer)
  525. ; variable Address pre-initialized to 0000
  526.  
  527. ProcHex3:            ;force disk read on first pass
  528.     mov SI, offset HexBuffer
  529.     mov word ptr InBufTail, SI   ;end of input buffer = start of buffer
  530.     inc SI                       ; SI = end + 1
  531.  
  532. ;----------------    main hex processing routine  --------------
  533.  
  534. HexPar:                    ;process next paragraph
  535. ;look for paragraph start char (':')
  536.     call GetChar     ;routine gets next char from input buffer & puts in AL, 
  537.                      ; loads from disk if needed.
  538.     jnc HexPar0
  539.     jmp HexFinish    ;Carry flag set indicates End of file
  540.  
  541. HexPar0:
  542.     cmp AL, ParStart
  543.     jz  HexPar2      ;paragraph start found, get next char
  544.     cmp AL, CR       ;end of line?
  545.     jnz HexPar1      ;no, check for next
  546. ;Eol, increment line counter
  547.     mov AX, LineCount      ;first echo current line #
  548.     call writeCR           ;return csr to beginning of line (regs preserved)
  549.     call WriteNum          ;display value in AX as decimal number on screen
  550.     inc AX                 ;increment line counter.
  551.     mov word ptr LineCount, AX
  552.     jmp short HexPar       ;keep looking for paragraph mark
  553.  
  554. HexPar1:
  555.     cmp AL, Eof      ;end of file?
  556.     jnz HexPar       ;no, keep looking
  557.     jmp HexFinish    ;End of file, finish
  558.  
  559. HexPar2:    ;paragraph marker found,  now process hex paragraph.
  560.   ;GetHexByte routine reads 2 bytes as ASCII chars, converts to byte value in AL
  561.   ;Non-hex digits (0-9, A-F, a-f) are ignored.
  562.   ;Keep check sum (CRC) in BL.
  563.  
  564.     call GetHexByte  ;get # data bytes: AL = # bytes in this paragraph
  565.     jc  HexParExit   ;Cy set if Eof encountered 
  566.     mov BL, AL       ;1st value into CRC
  567.     cbw              ;convert byte value in AL into word value in AX
  568.     mov word ptr NumBytes, AX    ;save # bytes
  569.  
  570. ;get address from file: 16-bit word
  571.     call GetHexByte  ;high byte of address in AL
  572.     jc  HexParExit   ;Cy set if Eof encountered 
  573.     add BL, AL       ;add to CRC
  574.     mov CH, AL       ;high byte into hi-order part of CX
  575.     call GetHexByte  ;AL = low order byte
  576.     jc  HexParExit   ;Cy set if Eof encountered 
  577.     add BL, AL       ;update CRC
  578.     mov CL, AL       ;low byte into lo-order part of CX; CX = address from file
  579. ;check to see if any data bytes indicated in this paragraph
  580.     mov AX, NumBytes 
  581.     or  AX, AX       ;# bytes = 0?
  582.     jz  HexPar4     ;skip address check if no data bytes indicated
  583. ;else, check calc'd address against hex file's value
  584.     mov AX, Address  ;address as calculated from # bytes read
  585.     cmp AX, CX       ;do they agree?
  586.     je  HexPar3      ;Ok
  587. ;no, don't agree, display warning but continue with calc'd value
  588.     push AX          ;save needed regs used in write 
  589.     push BX
  590.     push DX
  591.     push CX          ;CX = address derived from file
  592. ;explicitly echo line # & paragraph #
  593.     call writeCR           ;return cursor to beginning of line
  594.     mov AX, LineCount      ;echo line # & paragraph #
  595.     call WriteNum
  596.     mov AX, ParaCount
  597.     call WriteNum
  598.     write StdOut, AddrErrMsg, AddrErrLen
  599.     mov ErrorCode, 1       ;return code for 'serious' error (is it?)
  600.     pop CX           ;retrieve file's address
  601.     mov AX, CX       ; & display it
  602.     call WriteNum    ; on screen (future version: show hexadecimal address)
  603.     call NewLine     ;CRLF preserving regs
  604.     pop DX           ;retrieve rest of regs
  605.     pop BX
  606.     pop AX
  607.  
  608. HexPar3:         ;increment address counter by # bytes in this paragraph
  609.     mov CX, NumBytes
  610.     add AX, CX
  611.     mov word ptr Address, AX
  612.  
  613. HexPar4:            ;get paragraph attribute
  614.     call GetHexByte
  615.     jc  HexParExit      ;Cy set if Eof encountered 
  616.     add BL, AL          ;increment CRC
  617.     cmp AL, 0           ;non-zero means last paragraph
  618.     jz  HexPar5
  619.     mov byte ptr LastParagr, AL  ;store code as signal
  620. HexPar5:
  621.     mov CX, NumBytes    ;load loop counter with # data bytes
  622.     cmp CX, 0           ;no data bytes?
  623.     jz  HexPar8         ;skip if no data bytes
  624.     jmp short HexPar6   ;skip past HexParExit routine
  625.  
  626. HexParExit:             ;stick this in here so relative jumps are within range
  627.     write StdOut, PrematureMsg, PrematureLen
  628.     mov ErrorCode, 1    ;code for serious error. Probably not Hex format.
  629.     jmp short HexFinish
  630.  
  631. HexPar6:    ;store data bytes in output buffer
  632.             ;DI= curr pos in out buffer, end = BinBuffer + BinBufSize
  633.     mov DX, offset BinBuffer
  634.     add DX, BinBufSize     ;DX = BinBuffer+BinBufSize = addr of end of buffer+1
  635.     dec DX                 ;DX = addr of last position in output buffer
  636.     cmp DI, DX             ;are we beyond?
  637.     jbe HexPar7            ;no, Ok, skip
  638. ;no room, flush buffer to disk
  639.     mov BP, offset BinBuffer     ;point to output buffer
  640.     call WriteOutput             ; & write to disk (regs are preserved, DI = BP)
  641.     jc  HexFinish          ;carry flag set means write error; abort
  642. HexPar7:
  643.     call GetHexByte  ;get next value from input buffer (AL=byte, BX-DX preserved)
  644.     jc  HexParExit   ;Cy set if Eof encountered 
  645.     stosb            ;store value in output buffer.
  646.     add BL, AL       ;add value to CRC
  647.     loop HexPar6
  648.  
  649. HexPar8:         ;check CRC
  650.     neg BL           ;take 2's complement
  651.     call GetHexByte  ;AL = file's CRC
  652.     jc  HexParExit   ;Cy set if Eof encountered 
  653.     cmp AL, BL       ;compare with calc'd CRC
  654.     je HexPar9       ;OK, they agree.
  655. ;CRC's don't agree, show err msg but continue
  656.     push BX
  657.     push CX
  658.     push DX
  659.     call writeCR           ;return cursor to beginning of line
  660.     mov AX, LineCount      ;echo line # & paragraph #
  661.     call WriteNum
  662.     mov AX, ParaCount
  663.     call WriteNum
  664.     write StdOut, CRCerrMsg, CRCerrLen
  665.     mov ErrorCode, 1        ;code for serious error -- this is the whole point
  666.                             ;of Intel Hex format, after all.
  667.     pop DX
  668.     pop CX
  669.     pop BX
  670.  
  671. HexPar9:
  672.     mov AX, ParaCount      ;echo paragraph #
  673.     call WriteNum
  674.     inc AX                 ;increment paragraph counter
  675.     mov word ptr ParaCount, AX
  676.  
  677.     jmp HexPar   ;look for next paragraph [long jump]
  678.  
  679. HexFinish:       ;finish hex file, flush buffer to disk
  680.     mov BP, offset BinBuffer
  681.     call WriteOutput
  682.     jmp short FinishOff
  683.  
  684. ProcHex endP
  685. subttl  --  Main procedure --
  686. ;++++++++++++++++
  687.  
  688. FinishOff:       ;end of input file reached. Close it up
  689.     mov BX,InFile                ;close input file
  690.    call CloseFile
  691.     jnc FinishOff1
  692. ;routine will set Cy if error
  693.     mov byte ptr ErrorCode,1     ;set flag for serious error
  694.     write StdOut,CloseErrMsg,CloseErrLen
  695.     echoZ InFileName
  696.  
  697. FinishOff1:          ;close output file
  698.     mov BX, OutFile  ;file handle
  699.     cmp BX,0         ;Output file opened yet?
  700.     jz  Exit         ;if not, just exit
  701.     call CloseFile   ; else, close file first
  702.     jnc Exit
  703.     write StdOut, CloseErrMsg, CloseErrLen
  704.     echoZ OutFileName
  705.     jmp short ErrExit      ;exit with error code
  706.  
  707. FatalError:                ;something inexplicable occurred
  708.     write StdErr, FatalErrMsg, FatalErrLen
  709.  
  710. ErrExit:                   ;'generic' error exit
  711.     mov AL,1               ;exit with return code 1 (fatal error) for DOS 
  712.     DOScall Terminate      ; ERRORLEVEL check
  713.  
  714. Exit:
  715.     mov AL,ErrorCode       ;get error code from memory
  716.                            ;if error,  return code will be set accordingly
  717.                            ;else, exit to DOS with 0 return code (AL=0)
  718.     DOScall Terminate      ; for 'no error'.
  719.  
  720. Main    endP
  721.  
  722. subttl  --  Subroutines  --
  723. ;---  End of Main Program  ---
  724. page
  725. ;+++  Subroutines  +++
  726.  
  727. Upcase  proc near    ;convert char in AL to upper case
  728.     cmp AL,'a'       ;is it lower case letter?
  729.     jb  UpcaseExit   ;no
  730.     cmp AL,'z'
  731.     ja  UpcaseExit
  732.     sub AL,'a'-'A'   ;convert to upper case
  733. UpcaseExit:
  734.     ret
  735. UpCase  endP
  736. ;++++++++++++++++++++
  737.  
  738. CommandArgs proc near      ; get command line arguments
  739.     ;destroys SI, DI, AL
  740.     ; returns carry flag clear if successful, Cy=1 if not
  741.  
  742.     mov byte ptr ProcCode, 0     ;pre-set code to process binary --> hex by default
  743.  
  744.     mov SI, CommandTail          ;DS:SI points to command line address
  745.     mov DI, offset InFileName    ;ES:DI points to filename location
  746.     cld              ;direction flag upward
  747.     lodsb            ;get 1st byte -- contains # chars in cmd tail
  748.     or AL,AL         ;anything there?
  749.     jz CAerr         ;if not, exit with error stat
  750.  
  751. CommandArgs1:        ;else, read it. Skip over blanks to 1st char
  752.     lodsb            ;read byte at [SI] into AL
  753.     cmp AL,CR        ;CR or switch command with no file spec is 'error'
  754.     je  CAerr        ; exit with explanatory msg
  755.     cmp AL, SwitchChar
  756.     je  CAerr
  757.     cmp AL,Blank     ;is it a blank?
  758.     jbe CommandArgs1 ;skip blanks [and control chars]
  759.  
  760. ;next char
  761. CommandArgs2:        ;found char
  762.     cmp AL,'?'       ;question mark?
  763.     je  CAerr        ;can't be file spec, exit with error to force explanation
  764.    call Upcase       ;convert char in AL to upper case
  765.     stosb            ;store it in filename buffer
  766.     lodsb            ;get next char
  767.     cmp AL, CR             ;carriage return yet?
  768.     je  CAexit             ;yes, end of command tail
  769.     cmp AL, SwitchChar     ;command switch?
  770.     je  CommandArgs4       ;yes, end of file spec
  771.     cmp AL, Blank          ;blank?
  772.     jne CommandArgs2       ;no, go back for more chars
  773.  
  774. ;process switch commands
  775. CommandArgs3:
  776. ;char was blank, check for more commands
  777.     lodsb            ;get next char in cmd tail
  778.     cmp AL,CR        ;CR?
  779.     je  CAexit       ;yes, no more commands.
  780.     cmp AL,Blank     ;blank?
  781.     je  CommandArgs3       ;skip over blanks and non-switch chars
  782.     cmp AL,SwitchChar      ;switch command ('/')?
  783.     jne CommandArgs3       ;until switch found
  784. CommandArgs4:        ;switch was found
  785.     lodsb            ;get next char
  786.    call Upcase       ;convert to upper case
  787.     cmp AL,HexCode   ;is it command to process a hex file?
  788.     jne CommandArgs5 ;no, check other switches
  789.     mov byte ptr ProcCode, HexCode     ;non-zero indicates process hex file
  790.     jmp short CommandArgs3             ;Look for next command
  791.  
  792. CommandArgs5:              ;put other switch code checks here
  793.  
  794. ;char after switch doesn't match a command: not really an error, just show msg & ignore
  795.     mov byte ptr SwitchCode, AL                ;put char in position in BadSwitchMsg
  796.     write StdOut, BadSwitchMsg, BadSwitchLen   ; & display it
  797.     jmp short CommandArgs3                     ;Look for next command
  798. ;ignore rest of command tail 
  799.  
  800. CAexit:              ;normal exit
  801.     clc              ;clear carry flag
  802.     ret
  803. CAerr:               ;'error' occured (nothing on cmd line)
  804.     stc              ;set carry flag
  805.     ret
  806. CommandArgs endP
  807. subttl  --  Subroutines --  file-related  --
  808. ;+++++++++++++++++++++
  809.  
  810. OpenInput   proc near      ;add default extensions & open input file
  811.     ;Input file is AsciiZ string at InFileName
  812.     ;if no extension is entered, the routine tries to open input file with
  813.     ; one of two default extensions.
  814.     ;on entry, BP = address of 1st default extension; the 2nd is stored right after it.
  815.     ;on exit,  Cy flag will be set if error, clear if Ok
  816.     ;AX, CX, & SI are altered
  817.  
  818. ;check if extension was entered
  819.     cld
  820.     lea SI, InFileName     ;point to start of string
  821. OpenInput1:
  822.    lodsb            ;get char
  823.    cmp AL,0         ;end of string?
  824.    jz  OpenInput2
  825.    cmp AL,Dot       ;is it '.'?
  826.    jnz OpenInput1          ;no, get next char
  827. ;file spec includes a dot, assume an extension was intended
  828.     jmp OpenInput3          ;skip to open file
  829.  
  830. OpenInput2:        ;no dot in file spec, add default extension
  831.             ;NOTE that DI points to end of AsciiZ string from previous op
  832.     mov SI, BP       ;address of 1st default ext passed to subroutine in BP
  833.     mov CX,4               ;# bytes in '.xxx'
  834.     rep movsb              ;put extension in place
  835. ;try to open the file
  836.    call OpenInFile         
  837.     jnc OpenInputexit             ;open successful
  838. ;couldn't open 1st default, try alternate ext
  839.     sub DI,4               ;back up to extension position again
  840. ;NOTE:  SI is pointing to 2nd default ext, stored immediately after 1st
  841.     mov CX,4               ;# bytes in '.xxx'
  842.     rep movsb              ;put extension in place
  843. ;and proceed
  844.  
  845. OpenInput3:
  846.    call OpenInFile          ;try to open the file for reading
  847.     jnc OpenInputexit       ;open successful, return
  848.  
  849. ;error occurred, display message
  850. ;for future version, could use code in AX for more specific msgs
  851.  
  852.     write StdOut, OpenErrMsg, OpenErrLen       ;generic error msg for simplicity
  853.     echoZ InFileName                           ;echo filename
  854.  
  855. OpenInputErrExit:           
  856.     stc              ;carry indicates error in open
  857.     ret
  858. OpenInputexit:       ;normal exit,
  859.     clc              ;clear carry flag
  860.     ret
  861. OpenInput   endP
  862.  
  863. ;+++++++++++++++++++++
  864. OpenInFile  proc near            ;open file for input
  865.     mov DX,offset InFileName     ;DS:DX points to asciiz filename
  866.     sub AL, AL             ;AL=0 access is read only
  867.     DOScall FileOpen
  868.     jnc OifExit            ;if error, AX = error code, Cy set
  869.     ret
  870. OifExit:
  871.     mov word ptr InFile,AX       ;if successful, AX=file handle
  872.     ret                    
  873. OpenInFile  endP
  874.  
  875. ;+++++++++++++++++++++
  876. OpenOutFile proc near      ;create file for output
  877. ;on entry, BP = address of extension for output file
  878. ; regs AX-DX, SI, DI are altered
  879.  
  880. ;copy InFileName 
  881. ; look for '.' in extension
  882.     cld                    ;direction upward
  883.     lea DI, InFileName     ;point to start of string
  884.     mov CX,DOSpath         ;max # chars in file name string
  885.     mov AL,Dot             ;char to scan for
  886.     repne scasb            ;scan for byte in AL
  887.     neg CX                 ;find position;  pos('.')=DOSpath - CX
  888.     add CX,DOSpath         ;CX= -CX + DOSpath
  889.     jz  OofErr       ;end reached, no dot = error at this point
  890. ;else, CX now contains offset of dot
  891.     dec CX           ;leave off the dot
  892.     lea SI, InFileName
  893.     lea DI, OutFileName
  894.     repe movsb       ;copy InFileName to OutFileName
  895.     mov SI, BP       ;BP= address of default extension for output file
  896.                      ;DI still points to end of filename
  897.     mov CX,4         ;length of '.xxx'
  898.     repe movsb       ;copy ext
  899. ;OutFileName now is AsciiZ str with same filename as InFile, but output default ext.
  900. ;create file for output
  901.     mov DX, offset OutFileName   ;DS:DX points to Asciiz
  902.     mov CX,0                     ;normal file attribute
  903.     DOScall FileCreate           ;create or truncate file function
  904.  
  905.     jnc OofExit
  906. OofErr:              ;error, display generic message
  907.     write StdOut, OpenErrMsg, OpenErrLen
  908.     echoZ OutFileName
  909.     ret              ;Cy set to indicate unsuccessful open
  910. OofExit:
  911.     mov word ptr OutFile, AX     ;else AX = file handle
  912.     ret                          ;store it & return
  913. OpenOutFile endP
  914.  
  915. ;+++++++++++++++++++++
  916. CloseFile  proc near       ;close file whose file handle is in BX
  917.     cmp BX,0               ;avoid inadvertently "close"ing keyboard
  918.     jnz CF1
  919.     stc                    ;set carry to indicate error
  920.     ret
  921. CF1:
  922.     DOScall FileClose      ;if successful, Cy=0
  923.     ret                    ;if error, AX = error code, Cy set
  924. CloseFile  endP
  925.  
  926. ;+++++++++++++++++++++
  927. ReadError   proc near      ;error occurred in reading file
  928.                            ; Alters regs AX-DX
  929.     write StdErr, ReadErrMsg, ReadErrLen   ;display generic read error msg
  930.     echoZ InFileName                       ; echo filename
  931.     mov ErrorCode, 1                       ;set error flag for FinishOff procedure
  932.     ret
  933. ReadError   endP
  934.  
  935. ;+++++++++++++++++++++
  936. WriteOutput proc near      ;write the contents of the output buffer to disk
  937.   ;on entry, BP = address of output buffer
  938.   ;NOTE: DI points to last byte in output buffer
  939.   ;BX = file handle          = OutFile
  940.   ;CX = # bytes to write     = DI - OutBuffer
  941.   ;DX = buffer area          = OutBuffer
  942.   ;on return, IF SUCCESSFUL,   AX= # bytes written; set Cy if AX < CX
  943.   ;           IF UNSUCCESSFUL, AX= error code (disregard), Cy set
  944.  
  945.     push AX      ;save all registers used
  946.     push BX
  947.     push CX
  948.     push DX
  949.  
  950.     mov DX, BP             ;starting address of buffer
  951.     mov CX, DI             ;calculate # bytes
  952.     sub CX, DX             ; = DI - OutBuffer
  953.     mov BX, OutFile
  954.     DOScall writeFile      ;function 40h
  955.     jc  WriteOut1          ;Cy set indicates error
  956.  
  957.     cmp AX, CX             ;# bytes written same as req'd ?
  958.     jz  WriteOut1          ;Ok
  959. ;# bytes not same may mean disk full, just display 'generic' error to keep it simple
  960.     write StdErr, WriteErrMsg, WriteErrLen
  961.     echoZ OutFileName
  962.     mov byte ptr ErrorCode, 1    ;Return code for serious error
  963.     stc                          ;Cy set signals error to calling routine
  964.  
  965. WriteOut1:
  966.     pop DX       ;restore registers
  967.     pop CX
  968.     pop BX
  969.     pop AX
  970.  
  971.     mov DI, BP   ;reset output buffer pointer
  972.  
  973.     ret
  974. WriteOutput endP
  975. subttl  --  Subroutines  for character processing  --
  976.  
  977. ;+++++++++++++++++++++
  978. ConvHex proc near    ;convert the byte in AL into ascii representation in
  979.                      ; AX, with Most Significant digit in AH, 
  980.                      ; Least Significant in AL
  981.    push dx
  982.     mov AH,AL        ;copy the value
  983. ;most significant
  984.     and AH,0Fh       ;mask off high nibble
  985.     mov DL,AH        ;use D register for calculation
  986.     call HexDigit    ;convert DL into ascii hex digit
  987.     mov AH,DL        ;MSB now in AH
  988. ;least significant
  989.     and AL,0F0h      ;mask off low nibble
  990.     shr AL,1         ;shift hi into lo
  991.     shr AL,1
  992.     shr AL,1
  993.     shr AL,1
  994.     mov DL,AL
  995.     call HexDigit
  996.     mov AL,DL        ;LSB in AL
  997.     pop dx
  998.     ret
  999. ConvHex endP
  1000.  
  1001. ;+++++++++++++++++++++
  1002. HexDigit    proc near      ;convert byte in DL into ascii hex digit
  1003.     add DL,'0'       ;add Ascii offset
  1004.     cmp DL,'9'       ;'A' - 'F' ?
  1005.     jbe HexDigit1    ;no, that's it.
  1006.     add DL,'A'-1-'9' ; else, correct for ascii offset to get A-F
  1007. HexDigit1: 
  1008.     ret
  1009. HexDigit    endP
  1010.  
  1011. ;+++++++++++++++++++++
  1012. GetHexByte  proc near      ;load 2 consecutive bytes from input buffer,
  1013.                            ; & convert to byte value in AL. 
  1014. ;routine converts to upper case; rejects all chars other than 0-9, A-F, a-f
  1015. ;ON ENTRY, DS:SI points to next char to read
  1016. ;ON EXIT,  AL = byte value; SI = position of next char, AH altered (=0)
  1017. ;          if Eof, AX = 0, carry flag set
  1018.  
  1019.     push BX      ;save regs
  1020.     push CX
  1021.     push DX
  1022.  
  1023. ;high byte
  1024.    call GetDigit       ;read next char & convert to binary value in AL
  1025.     jc  GetHexByteExit ;carry set to indicate end of file
  1026.  
  1027.     mov AH, AL         ;save value
  1028. ;low byte
  1029.     call GetDigit      ;read next char & convert to binary value in AL
  1030.     jc  GetHexByteExit ;carry set to indicate error
  1031.     shl AH, 1          ;arithmetic to reassemble byte value = 2^4 * AH + AL
  1032.     shl AH, 1
  1033.     shl AH, 1
  1034.     shl AH, 1          ;AH = 16 * AH
  1035.     add AL, AH         ;AL = AL + 16*AH
  1036.     cbw                ;convert byte in AL into word in AX
  1037.  
  1038. GetHexByteExit:
  1039.     pop DX       ;restore regs
  1040.     pop CX
  1041.     pop BX
  1042.  
  1043.     ret
  1044. GetHexByte  endP
  1045.  
  1046. ;+++++++++++++++++++++
  1047. GetDigit    proc near      ;get a hex digit from input buffer; ignore all chars other
  1048.                            ; than '0'..'9','A'..'F' (upper or lower case)
  1049. ;ON ENTRY,  SI=next byte to read
  1050. ;ON RETURN, if Eof,     AX = 0, Carry Flag set
  1051. ;           if not Eof, AL = valid hex digit (upper case), Cy clear
  1052.  
  1053.    call GetChar         ;get char into AL
  1054.     jc  GetDigitExit    ;carry flag set by GetChar to indicate end of file
  1055.    call UpCase          ;convert char in AL to upper case
  1056.  
  1057. ;check for valid char in ['0'..'9', 'A'..'F']
  1058.     cmp AL, '0'
  1059.     jb  GetDigit     ;< '0' no good, get next char
  1060.     cmp AL, 'F'
  1061.     ja  GetDigit     ;> 'F' n.g.
  1062.     cmp AL, '9'
  1063.     jbe GetDigit1    ;numeral between 0 and 9
  1064.     cmp AL, 'A'
  1065.     jb  GetDigit     ;invalid char between '9' & 'A' = '['..'`'
  1066.     sub AL, 7        ;valid hex digit A-F, adjust for diff between 'A' & '9'
  1067.  
  1068. GetDigit1:           ;valid hexadecimal digit (adjusted if necessary)
  1069.     sub AL, '0'      ;convert to binary value
  1070.     clc              ;clear carry flag for exit
  1071.  
  1072. GetDigitExit:        ;if EOF, Carry flag set on exit
  1073.     ret
  1074. GetDigit endP
  1075.  
  1076. ;+++++++++++++++++++++
  1077. GetChar proc near    ;get next char from input buffer, reading more from disk
  1078.                      ; if end of input buffer reached.
  1079. ;ON ENTRY, SI = address of next position in input buffer
  1080. ;ON EXIT,  AL = next char, SI incremented
  1081. ; if Eof,  AX = 0, carry flag set, SI unchanged
  1082. ;   regs AX-DX, SI changed
  1083.  
  1084. ;have we reached the end of the input buffer yet?
  1085.     mov CX, InBufTail      ;addr of last byte of data
  1086.     cmp SI, CX             ;beyond end yet?
  1087.     jbe GetChar1           ;Ok, get next char from buffer
  1088.                  ;else read another block from disk
  1089. ;+++++++++++++++++++++
  1090. ReadHexBlock proc near     ;get a block of data from input file
  1091.                            ;uses regs AX-DX, SI
  1092. ;if Eof, AX = 0
  1093. ;else  SI= start of buffer, InBufTail= end of buffer area (= AX)
  1094. ;      CX= # bytes requested
  1095.  
  1096.     push AX          ;save regs changed in proc
  1097.     push BX
  1098.     push CX
  1099.     push DX
  1100.  
  1101.     mov BX, InFile         ;input file handle
  1102.     mov CX, HexBufSize     ;  "    "   buffer size = # bytes to read
  1103.     lea DX, HexBuffer      ;point to input buffer to store data
  1104.                            ;NOTE: file pointer automatically adjusted by DOS
  1105.     DOScall ReadFile
  1106.     jnc ReadHexBlock3      ;Cy flag set if error
  1107.     call ReadError         ;error, display msg
  1108.     jmp FatalError         ; & exit without further ado.
  1109.  
  1110. ReadHexBlock3:             ;read Ok, no error
  1111.     cmp AX, 0              ;end of file?  (AX = # bytes actually read)
  1112.     jnz ReadHexBlock4      ;if End of file, AX= 0
  1113.     stc                    ;Eof, exit with carry flag set to signal calling routine
  1114.     jmp short ReadHexBlockExit   
  1115.  
  1116. ReadHexBlock4:       ;reset variables for start of data block
  1117. ;calculate end of data in input buffer =start addr + # bytes - 1
  1118.     lea SI, HexBuffer      ;point to start of input buffer
  1119.     add AX, SI             ;# bytes + address of buffer
  1120.     dec AX                 ;AX = # bytes + StartAddress - 1 = end of input buffer
  1121.     mov word ptr InBufTail, AX   ;store address of last data char
  1122.  
  1123. ReadHexBlockExit:
  1124.     pop DX           ;restore regs
  1125.     pop CX
  1126.     pop BX
  1127.     pop AX
  1128.  
  1129. ReadHexBlock endP    ;note use of proc/endP here only for neatness; not a subroutine
  1130. ;+++++++++++++++++++++
  1131.  
  1132.     jc  GetCharExit  ;Eof signaled by Cy flag
  1133.  
  1134. GetChar1:
  1135.     lodsb        ;load next char from input buffer into AL
  1136.     clc          ;clear carry for not EoF
  1137.  
  1138. GetCharExit:
  1139.     ret
  1140. GetChar endP
  1141.  
  1142. ;+++++++++++++++++++++
  1143.  
  1144. WriteNum    proc near      ;display the 16-bit # in AX on screen as decimal #
  1145.                            ;AX-DX are used, but preserved
  1146.     push AX
  1147.     push BX
  1148.     push CX
  1149.     push DX
  1150.  
  1151.     push AX          ;save value to be displayed since DOScall uses AH
  1152.     mov DL, Blank    ;precede # with a blank
  1153.     DOScall TTYout
  1154.     pop AX           ;AX = value to be displayed
  1155.  
  1156.     mov CX, 10d  ;the denominator (change this value for other bases)
  1157.     call Bin2Dec ;convert to decimal #. ON RETURN,
  1158.                  ;    CH = 10000's     CL destroyed
  1159.                  ;    BH =  1000's     BL = 100's
  1160.                  ;    DH =    10's     DL =   1's
  1161.     push DX          ;save decade counter values during DOS function call
  1162.     cmp CH, 0        ; is # > 9999?
  1163.     jnz WriteNum1          ;avoid printing a '0' for numbers < 10,000
  1164.     mov CH, Blank-'0'      ;put a value in CH that will result in writing a blank
  1165.                            ; when EchoDig adds '0' value
  1166. WriteNum1:
  1167.     mov DL, CH       ;write 10000's digit
  1168.     call EchoDig
  1169.     mov DL, BH       ;1000's
  1170.     call EchoDig
  1171.     mov DL, BL       ; 100's
  1172.     call EchoDig
  1173.     pop CX           ;retrieve 10's & 1's values (pushed on the stack as DX) in CX
  1174.     mov DL, CH       ;  10's
  1175.     call EchoDig
  1176.     mov DL, CL       ;   1's
  1177.     call EchoDig
  1178.  
  1179.     pop DX
  1180.     pop CX
  1181.     pop BX
  1182.     pop AX           ;note: routine returns with original value unchanged
  1183.     ret
  1184. WriteNum    endP
  1185.  
  1186. ;+++++++++++++++
  1187. EchoDig proc near          ;convert binary value of decimal digit to ASCII digit
  1188.                            ;DL = binary value (0-9) of digit to display
  1189.     add DL, '0'      ;convert to ASCII
  1190.     DOScall TTYout   ;send to console
  1191.     ret
  1192. EchoDig endP
  1193.  
  1194. ;+++++++++++++++
  1195. Bin2Dec proc near    ;Convert 16-bit word in AX into decimal equivalent for display.
  1196.                      ;Use 'bins'technique: progressively subtract ten from number,
  1197.                      ; storing number in bins corresponding to 1,10,100,1000,10000.
  1198. ;ON ENTRY, AX = 16-bit integer, CX (CL) = divisor (10d)
  1199. ;ON EXIT,  CH = 10000              (NOTE: maximum number is 65535)
  1200. ;          BH =  1000  BL = 100
  1201. ;          DH =    10  DL =   1    CL is destroyed on exit
  1202. ;all other regs are preserved.
  1203.  
  1204.     push AX      ;save input value
  1205.     push BP      ;will use BP for temporarily storing 10000 figure
  1206.     xor BX, BX   ;initialize bin counters to zero
  1207.     xor DX, DX
  1208.     xor BP, BP
  1209.     xor CH, CH   ;probably unnecessary CH=0
  1210.  
  1211. Bin2Dec1:
  1212.     cmp AX, CX   ;remainder < denominator?
  1213.     jb  Bin2DecExit  ;yes, done, exit
  1214.     sub AX, CX   ; subtract divisor (10d)
  1215.     inc DH       ; increment 10's counter
  1216.     cmp DH, CL   ;10's ctr overflow?
  1217.     jb  Bin2Dec1 ;no, keep going
  1218.     inc BL       ; increment 100's ctr 
  1219.     xor DH, DH   ; & reset 10's
  1220.     cmp BL, CL   ; 100's ctr overflow?
  1221.     jb  Bin2Dec1
  1222.     inc BH       ;increment 1000's
  1223.     xor BL, BL
  1224.     cmp BH, CL   ;1000's overflow?
  1225.     jb  Bin2Dec1
  1226.     inc BP
  1227.     xor BH, BH
  1228.     cmp BP, CX   ;10000's overflow?
  1229.     jb  Bin2Dec1
  1230. ;should never reach this point
  1231.     jmp FatalError   ;this can't happen -- something is very wrong.
  1232.  
  1233. Bin2DecExit:
  1234.     mov DL, AL   ;value of 1's
  1235.     mov CX, BP   ;store value of 10000 in CH
  1236.     mov CH, CL
  1237.     pop BP       ;restore regs
  1238.     pop AX
  1239.     ret
  1240. Bin2Dec endP
  1241. ;+++++++++++++++++++++
  1242.  
  1243. EchoString  proc near      ;dumps asciiz string; DS:SI points to head
  1244.     push CX                ;save registers (note: AH is trashed)
  1245.     push DX
  1246.     mov AH, TTYout         ;set up for Int 21h to write char in DL
  1247.     mov CX,HexBufSize      ;loop for finite # in case string not terminated with 0
  1248.  
  1249. EchoString1:
  1250.     mov DL,[SI]
  1251.     cmp DL,0
  1252.     jz  EchoStringExit     ;terminate on binary 0
  1253.     int 21h                ;else display it
  1254.     inc SI                 ;bump pointer
  1255.     loop EchoString1       ;loop rather than jmp in case string not really AsciiZ
  1256.     
  1257. EchoStringExit:
  1258.     call NewLine     ;CRLF
  1259.     pop DX           ;restore registers
  1260.     pop CX
  1261.     ret
  1262. EchoString  endP
  1263.  
  1264. ;+++++++++++++++++++++
  1265. EchoAddr    proc near      ;echo [current address paragraph] pointed to by DI
  1266.         ;DI=one byte past last byte entered, 
  1267.  
  1268.     push AX          ;save registers used
  1269.     push BX
  1270.     push CX
  1271.     push DX
  1272.  
  1273.     mov DL,CR        ;return cursor to start of line
  1274.     DOScall TTYout
  1275.  
  1276.     mov CX, 4        ;4 bytes to give 16-bit word in Ascii
  1277.     mov DX, DI       ;point to string DX=end+1
  1278.     sub DX,CX        ;DX=start of string
  1279.     mov BX, ConOut   ;console out, no redirection
  1280.     DOScall writeFile
  1281.  
  1282.     pop DX           ;restore original values
  1283.     pop CX
  1284.     pop BX
  1285.     pop AX
  1286.     ret
  1287. EchoAddr    endP
  1288.  
  1289. ;+++++++++++++++++++++
  1290. writeCR proc near    ;send CR to console
  1291.     push AX
  1292.     push DX
  1293.     mov DL, CR
  1294.     DOScall TTYout
  1295.     pop DX
  1296.     pop AX
  1297.     ret
  1298. writeCR endP
  1299.  
  1300. ;+++++++++++++++++++++
  1301. NewLine proc near    ;send CR LF to console, preserving regs
  1302.     push AX
  1303.     push DX
  1304.     mov DL, CR
  1305.     DOScall TTYout
  1306.     mov DL, LF
  1307.     DOScall TTYout
  1308.     pop DX
  1309.     pop AX
  1310.     ret
  1311. NewLine endP
  1312. ;+++++++++++++++++++++
  1313. subttl  --  Data buffers, messages  --
  1314. page
  1315. ;---  messages  ---
  1316.  
  1317. DOSerr  db 'Wrong DOS version.  Need DOS 2.0 or greater. BINTEL.COM ',Version
  1318.         db CR,LF,'$'
  1319.  
  1320. HelpMsg db CR,LF
  1321.   db 'Usage:  BINTEL  InputFile [/H]', CR,LF
  1322.   db '  where InputFile = file to be processed.',CR,LF
  1323.   db LF
  1324.   db 'If no /h switch entered, the program converts a binary file to intel hex.',CR,LF
  1325.   db 'If switch /h is entered, the program converts a hex file into binary.',CR,LF
  1326.   db LF
  1327.   db 'Default extensions:',CR,LF
  1328.   db '  BIN,COM for binary files;  HEX for Intel Hex files.',CR,LF
  1329.   db LF
  1330.   db 'Examples:', CR,LF
  1331.   db '[assume TEST1.BIN and TEST1.COM both exist]',CR,LF
  1332.   db LF
  1333.   db '  bintel test1      converts   TEST1.BIN to    TEST1.HEX',CR,LF
  1334.   db '  bintel test1.com  converts   TEST1.COM to    TEST1.HEX',CR,LF
  1335.   db '  bintel test2.exe  converts   TEST2.EXE to    TEST2.HEX',CR,LF
  1336.   db '  bintel hexfile /h converts HEXFILE.HEX to  HEXFILE.BIN',CR,LF
  1337.   db '  bintel \bin\foo.b converts  \BIN\FOO.B to \BIN\FOO.HEX',CR,LF
  1338.   db '  bintel bar.foo /h converts     BAR.FOO to      BAR.BIN',CR,LF
  1339.   db LF
  1340.   db 'BINTEL.COM is donated to the public domain.'
  1341.   db CR, LF
  1342. HelpLen equ $-HelpMsg
  1343.  
  1344. BadSwitchMsg db 'Unrecognized command switch: /'
  1345. SwitchCode   db 0, CR, LF
  1346. BadSwitchLen equ $-BadSwitchMsg
  1347.  
  1348. OpenErrMsg  db CR, 'Error trying to open file:   '
  1349. OpenErrLen  equ $-OpenErrMsg
  1350.  
  1351. ReadErrMsg  db CR, LF, 'Error reading file:          '
  1352. ReadErrLen  equ $-ReadErrMsg
  1353.  
  1354. WriteErrMsg db CR, LF, 'Error writing file:          '
  1355. WriteErrLen equ $-WriteErrMsg
  1356.  
  1357. CloseErrMsg db CR, LF, 'Error trying to close file:  '
  1358. CloseErrLen equ $-CloseErrMsg
  1359.  
  1360. ProcBinMsg  db CR, 'Processing binary file:      '
  1361. ProcBinLen  equ $-ProcBinMsg
  1362.  
  1363. BinHead     db CR, 'Address', CR, LF
  1364.             db     '-------', CR, LF
  1365. BinHeadLen  equ $-BinHead
  1366.  
  1367. ProcHexMsg  db CR, 'Processing [Intel Hex] file: '
  1368. ProcHexLen  equ $-ProcHexMsg
  1369.  
  1370. HexHead     db CR, 'Line #  Para #', CR, LF
  1371.             db     '------  ------', CR, LF
  1372. HexHeadLen  equ $-HexHead
  1373.  
  1374. OverflowMsg db ' *** WARNING: address counter overflowed. ', Bel, CR, LF
  1375. OverflowLen equ $-OverflowMsg
  1376.  
  1377. AddrErrMsg  db ' *** WARNING: address counter disagrees with file: ', Bel
  1378. AddrErrLen  equ $-AddrErrMsg
  1379.  
  1380. CRCerrMsg   db ' *** WARNING: calculated CRC disagrees with file. ', CR, LF
  1381. CRCerrLen   equ $-CRCerrMsg
  1382.  
  1383. PrematureMsg db CR, LF, ' *** End of file encountered before end of paragraph. *** '
  1384.              db Bel,CR, LF
  1385. PrematureLen equ $-PrematureMsg
  1386.  
  1387. FatalErrMsg db CR,LF,' *** FATAL ERROR -- ABORTING -- BINTEL.COM *** ',Bel,CR,LF
  1388. FatalErrLen equ $-FatalErrMsg
  1389.  
  1390. ;---  Data Buffers ---
  1391.  
  1392. even    ;make buffer start on even address for DeskPro (8086)
  1393. BinBuffer   db BinBufSize dup (?)   ;NOTE: BinBuffer always even multiple of 16d
  1394. HexBuffer   db HexBufSize dup (?)
  1395. ;---------------------
  1396.  
  1397. Code    endS
  1398. ;================
  1399.         end Main
  1400.